#ifndef __MY_DATA__
#define __MY_DATA__
#include "util.hpp"
#include <cstdlib>
#include <mutex>
#include <mariadb/mysql.h>

/*
create table if not exists tb_video(
id int primary key auto_increment comment '视频ID',
name varchar(32) comment '视频名称',
info text comment '视频描述',
video varchar(256) comment '视频文件url，加上静态资源根目录就是实际存储路径',
image varchar(256) comment '封面图片文件url，加上静态资源根目录就是实际存储路径'
);
*/

namespace vod
{
#define HOST "127.0.0.1"
#define USER "root"
#define PASS ""
#define NAME "vod_system"

    static MYSQL *MysqlInit()
    {
        MYSQL *mysql = mysql_init(nullptr);
        if (mysql == nullptr)
        {
            std::cout << "init mysql instance failed:>" << mysql_error(mysql) << std::endl;
            return nullptr;
        }

        bool ret = mysql_real_connect(mysql, HOST, USER, PASS, NAME, 0, nullptr, 0);
        if (!ret)
        {
            std::cout << "connect mysql server failed:>" << mysql_error(mysql) << std::endl;
            mysql_close(mysql);
            return nullptr;
        }
        mysql_set_character_set(mysql, "utf8");

        return mysql;
    }

    static void MysqlDestroy(MYSQL *mysql)
    {
        if (mysql != nullptr)
        {
            mysql_close(mysql);
        }

        return;
    }

    static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
    {
        int ret = mysql_query(mysql, sql.c_str());
        if (ret != 0)
        {
            std::cout << sql << std::endl;
            std::cout << "get mysql_query failed:>" << mysql_error(mysql) << std::endl;
            return false;
        }
        return true;
    }

    class TableVideo
    {
    public:
        TableVideo()
        {
            _mysql = MysqlInit();
            if (_mysql == nullptr)
            {
                exit(-1);
            }
        }

        ~TableVideo()
        {
            MysqlDestroy(_mysql);
        }

        bool Insert(const Json::Value &video)
        {
            // id name info video image
            std::string sql;
            sql.resize(4096 + video["info"].asString().size()); // 防止简介过长
            #define INSERT_VIDEO "insert tb_video values(NULL, '%s', '%s', '%s', '%s');"
            // 要对各个数据进行校验，因为不校验直接用就有可能出问题
            if (video["name"].asString().size() == 0)
            {
                return false;
            }
            if (video["info"].asString().size() == 0)
            {
                return false;
            }
            if (video["video"].asString().size() == 0)
            {
                return false;
            }
            if (video["image"].asString().size() == 0)
            {
                return false;
            }
            sprintf(&sql[0], INSERT_VIDEO,
                    video["name"].asCString(),
                    video["info"].asCString(),
                    video["video"].asCString(),
                    video["image"].asCString());
            
            return MysqlQuery(_mysql, sql);
        }

        bool Update(int video_id, const Json::Value &video)
        {
            // id name info video image
            std::string sql;
            sql.resize(4096 + video["info"].asString().size()); // 防止简介过长
            #define UPDATE_VIDEO "update tb_video set name = '%s', info = '%s' where id = %d;"
            // 要对各个数据进行校验，因为不校验直接用就有可能出问题
            if (video["name"].asString().size() == 0)
            {
                return false;
            }
            if (video["info"].asString().size() == 0)
            {
                return false;
            }
            sprintf(&sql[0], UPDATE_VIDEO,
                    video["name"].asCString(),
                    video["info"].asCString(),
                    video_id);
            
            return MysqlQuery(_mysql, sql);
        }

        bool Delete(const int video_id)
        {
            std::string sql;
            sql.resize(1024);
            #define DELETE_VIDEO "delete from tb_video where id = %d;"
            sprintf(&sql[0], DELETE_VIDEO, video_id);
            return MysqlQuery(_mysql, sql);
        }

        bool SelectAll(Json::Value *videos)
        {
            #define SELECTAll_VIDEO "select * from tb_video;"
            _mutex.lock();  // lock start 保护查询与保存结果到本地的过程
            bool ret = MysqlQuery(_mysql, SELECTAll_VIDEO);
            if (!ret)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (!res)
            {
                std::cout << "mysql store result failed!" << std::endl;
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();    // lock end

            int row_num = mysql_num_rows(res);
            for (int i = 0; i < row_num; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                Json::Value video;
                video["id"] = atoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];
                videos->append(video);
            }
            mysql_free_result(res);

            return true;
        }

        bool SelectOne(int video_id, Json::Value *video)
        {
            std::string sql;
            sql.resize(1024);
            #define SELECTONE_VIDEO "select * from tb_video where id = %d;"
            sprintf(&sql[0], SELECTONE_VIDEO, video_id);
            _mutex.lock();
            bool ret = MysqlQuery(_mysql, sql);
            if (!ret)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (!res)
            {
                std::cout << "mysql store result failed!" << std::endl;
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();

            int num_row = mysql_num_rows(res);
            if (num_row != 1)
            {
                std::cout << "have no data!" << std::endl;
                mysql_free_result(res);
				return false;
            }
            MYSQL_ROW row = mysql_fetch_row(res);
            (*video)["id"] = video_id;
            (*video)["name"] = row[1];
            (*video)["info"] = row[2];
            (*video)["video"] = row[3];
            (*video)["image"] = row[4];
            mysql_free_result(res);

            return true;
        }

        bool SelectLike(const std::string &key, Json::Value *videos)
        {
            std::string sql;
            sql.resize(1024);
            #define SELECTLIKE_VIDEO "select * from tb_video where name like '%%%s%%';"
            sprintf(&sql[0], SELECTLIKE_VIDEO, key.c_str());
            _mutex.lock();
            bool ret = MysqlQuery(_mysql, sql);
            if (!ret)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (!res)
            {
                _mutex.unlock();
                std::cout << "mysql store result failed!" << std::endl;
                return false;
            }
            _mutex.unlock();
            int row_num = mysql_num_rows(res);
            for (int i = 0; i < row_num; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                Json::Value video;
                video["id"] = atoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];
                videos->append(video);
            }
            mysql_free_result(res);

            return true;
        }

    private:
        MYSQL *_mysql;
        std::mutex _mutex;
    };
}

#endif
